#!/usr/bin/ruby
#
# $Id:$
#
# Copyright 2009 Fivefront Corporation. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Original code was written by Motonori Shindo (mshindo@fivefront.com)
#
# Notes:
#
# This code relies on RESTful version of open-uri written by Leonard
# Richardson. You can install this package via gem as follows:
#
#     $ sudo gem install rest-open-uri
# 
# For this program to work, ImageMagik needs to be installed. If you
# don't have "display" command on your machine, you have to install
# ImageMagik first. For more about ImageMagik, please see
#
#     http://www.imagemagik.org
#
# If you encounter a problem around REXML while running this code, you
# may have to apply the following two patches:
# 
#     http://redmine.ruby-lang.org/issues/show/342
#     http://redmine.ruby-lang.org/issues/show/553
#

require 'rubygems'
require 'rest-open-uri'
require 'rexml/document'
require 'digest/sha1'

VS_URI = 'https://api.vidoop.com/vs'
USERNAME = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # put your username here
PASSWORD = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # put your password here
CUSTOMER_ID = 'xxxxxxxx' # put your customer ID here
SITE_ID = 'xxxxx'        # put your SITE ID here
# USER_ID can be an arbitary string but it should uniquely identify
# the user and not disclose any sensitive information such as user's
# identity itself. Salted hash of user's identity would be a nice
# choice as a USER_ID.
USER_ID = Digest::SHA1.hexdigest('mysalt' + 'mshindo@fivefront.com')

class ImageShield 

  attr_reader :image_uri

  def initialize(bundle, args = {})
    @bundle = bundle
    @imageshield_length = args[:imageshield_length] || 2
    @order_matters = args[:order_matters] || false
    @width = args[:width] || 3
    @height = args[:height] || 3
    @image_code_color = args[:image_code_color] || "Red"
    @id = 0
    @image_uri = ""

    resource = 
     "#{VS_URI}/customers/#{CUSTOMER_ID}/sites/#{SITE_ID}/services/imageshield"

    if @bundle.length != @width * @height
      raise "bundle length must be width * height!"
    end

    body = "bundle=#{@bundle.join(':')}&" \
           "imageshield_length=#{@imageshield_length}&" \
           "order_matters=#{@order_matters}&" \
           "width=#{@width}&" \
           "height=#{@height}&" \
           "image_code_color=#{@image_code_color}&" \
           "user_id=#{USER_ID}"

    begin
      response = open(resource,
                      :method => :post, 
                      :http_basic_authentication => [USERNAME, PASSWORD],
                      :body => body)
    rescue OpenURI::HTTPError => e
      response_code = e.io.status[0].to_i
      if response_code == 404
        puts "ImageShiled service is no enabled for this site"
      else
        raise "unexpected response code #{response_code}"
      end
    end

    xml = response.read
    document = REXML::Document.new(xml)
    @id = REXML::XPath.first(document, '/imageshield/id').text
    @image_uri = REXML::XPath.first(document, '/imageshield/imageURI').text
  end

  def authenticate(passcode)
    
    resource = "#{VS_URI}/imageshields/#{@id}"

    body = "code=#{passcode}"

    begin
      response = open(resource,
                      :method => :post, 
                      :body => body)
    rescue OpenURI::HTTPError => e
      response_code = e.io.status[0].to_i
      if response_code == 200
        true
      elsif response_code == 430
        false
      elsif response_code == 409
        puts "This ImageShield has already been attempted\n"
        false
      elsif response_code == 410
        puts "This ImageShield has already expired\n"
        false
      else
        raise "unexpected response code #{response_code}"
      end
    end
  end

end


class Category

  attr_reader :ids, :names

  def initialize(args = {})
    @ids = []
    @names = {}

    resource = "#{VS_URI}/categories"
    begin
      response = open(resource, :method => :get) 
    rescue OpenURI::HTTPError => e
      response_code = e.io.status[0].to_i
      if response_code == 400
        puts "This request is invalid"
      else
        raise "unexpected response code #{response_code}"
      end
    end

    xml = response.read
    document = REXML::Document.new(xml)
    @ids = REXML::XPath.match(document, '//category/id').map {|e| e.text}
    nm = REXML::XPath.match(document, '//category/name').map {|e| e.text}
    alist = @ids.zip(nm)
    @names = Hash[*alist.flatten]
  end

end


if __FILE__ == $0

  category = Category.new
  ids = category.ids.dup

  choices = []
  width = 4
  height = 3
  length = 3

  for i in 1..(width * height) do
    choices << ids.delete_at(rand(ids.length))
  end

  ishield = ImageShield.new(choices, {:imageshield_length => length, 
                              :width => width, :height => height})

  img = ''
  open(ishield.image_uri) do |ifile|
    img = ifile.read
  end

  tmpf = Tempfile.new("imageshield") 
  tmpf.write(img)
  tmpf.flush

  system("display #{tmpf.path} &")

  puts "Your selection of category are:\n"
  for i in 0..(length-1) do
    puts "  - #{category.names[choices[i]]}\n"
  end

  puts "Please enter your passcode:"
  passcode = gets().chop

  if ishield.authenticate(passcode)
    puts "Authentication Succeeded!!\n"
  else
    puts "Authentication Failed!!\n"
  end

  tmpf.close

end
